#include <string.h>
#include "vmtype.h"
#include "tp_goodix_gt9xx.h"
#include "tp_i2c.h"
#include "vmdcl.h"
#include "vmdcl_gpio.h"
#include "vmdcl_eint.h"
#include "vmdrv_tp.h"
#include "vmlog.h"
#include "vmdatetime.h"


#define TPD_RESET_ISSUE_WORDAROUND
#define CTP_MAX_RESET_COUNT 3

const VMUINT8 gpio_ctp_reset_pin = 19;
const VMUINT8 gpio_ctp_i2c_scl_pin = 1;
const VMUINT8 gpio_ctp_i2c_sda_pin = 2;
const VMUINT8 gpio_ctp_eint_pin = 52;

VM_DCL_HANDLE gpio_ctp_eint_handle;

VMUINT32 CTP_DELAY_TIME = 100;

static VMUINT32* VSIM1_CON2 = (VMUINT32*)0xA0700188;
static VMUINT32* VSIM1_CON0 = (VMUINT32*)0xA0700180;

VMUINT8 gt9xx_config[] =
{
	0x43,0xF0,0x00,0xF0,0x00,0x0A,0x05,0x00,0x01,0x08,
	0x28,0x0F,0x50,0x32,0x03,0x05,0x00,0x00,0x00,0x02,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x03,0x06,
	0x28,0x00,0x31,0x0D,0x03,0x06,0x00,0x9B,0x44,0x0D,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,
	0x1B,0x19,0x41,0x94,0xC5,0x02,0x00,0x00,0x00,0x04,
	0xBB,0x1B,0x00,0x9A,0x21,0x00,0x80,0x28,0x00,0x69,
	0x31,0x00,0x58,0x3B,0x00,0x58,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,
	0x20,0x05,0x05,0x20,0x22,0x77,0x0F,0x00,0x00,0x00,
	0x50,0x28,0x12,0x10,0x0E,0x0C,0x0A,0x08,0xFF,0xFF,
	0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x0C,0x0A,0x08,0x06,0x05,0x04,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x02,
	0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0x47,0x01
};

void delay_ms(VMUINT32 millisecs){
   VMUINT32 timeStop;
   VMUINT32 timeStart;
   VMUINT32 Freq = 0;
   volatile VMUINT32 i;
   millisecs = millisecs*1000;

    timeStart = vm_time_ust_get_count();
    while( Freq  < millisecs)
    {
    	for(i=0;i<5000;i++){}
        timeStop = vm_time_ust_get_count();
        Freq = timeStop - timeStart + 1;
    }
}

VM_DRV_TP_BOOL CTP_I2C_write_byte(VMUINT16 ucBufferIndex, VMUINT8 pucData)
{
    return ctp_i2c_send( CTP_SLAVE_ADDR, ucBufferIndex, &pucData, 1);
}

static VM_DRV_TP_BOOL ctp_goodix_gt9xx_set_device_mode(VM_DRV_TP_DEVICE_MODE mode);


//static void ctp_goodix_gt9xx_power(VM_DRV_TP_BOOL ON)
void ctp_goodix_gt9xx_power(VM_DRV_TP_BOOL ON)
{
    static VM_DRV_TP_BOOL power_status = VM_DRV_TP_TRUE;

    if(ON == power_status)
    {
        return;
    }

    power_status = ON;

    if ( ON )
    {
        ctp_goodix_gt9xx_set_device_mode( VM_DRV_TP_ACTIVE_MODE );
    }
    else
    {
        ctp_goodix_gt9xx_set_device_mode( VM_DRV_TP_SLEEP_MODE );
    }
}

static VM_DRV_TP_BOOL ctp_goodix_gt9xx_get_information(ctp_info_t *ctp_info)
{
	VM_DRV_TP_BOOL ret;
	VMUINT8 cfg;

	ret = CTP_I2C_read( CTP_VERSION_INFO_REG, (VMUINT8 *)ctp_info, sizeof( ctp_info_t ) );
	ret = CTP_I2C_read(CTP_CONFIG_REG_BASE, &cfg, 1);

	return ret;
}

VM_DRV_TP_BOOL ctp_goodix_gt9xx_set_configuration( void )
{
	VM_DRV_TP_BOOL ret;
	VMUINT8 *cfg_p;
	ctp_info_t ctp_info;

	if ( ctp_goodix_gt9xx_get_information(&ctp_info) != VM_DRV_TP_TRUE )
	{
		return VM_DRV_TP_FALSE;
	}

	ret = CTP_I2C_send( CTP_CONFIG_REG_BASE, gt9xx_config , sizeof(gt9xx_config) );

	return ret;
}

static VM_DRV_TP_PEN_STATE ctp_goodix_gt9xx_hisr(void)
{
	VMUINT8 lvalue;

	CTP_I2C_read(CTP_TOUCH_INFO_REG_BASE, &lvalue, 1);

	if(lvalue & CTP_STAT_NUMBER_TOUCH)
	{
		return VM_DRV_TP_DOWN;
	}
	else
	{
		return VM_DRV_TP_UP;
	}
}

void ctp_init_EINT(void)
{

    vm_dcl_eint_control_config_t eint_config;
    vm_dcl_eint_control_sensitivity_t sens_data;
    vm_dcl_eint_control_hw_debounce_t deboun_time;
    vm_dcl_eint_control_auto_change_polarity_t auto_change;
    VM_DCL_STATUS status;

    memset(&eint_config,0, sizeof(vm_dcl_eint_control_config_t));
    memset(&sens_data,0, sizeof(vm_dcl_eint_control_sensitivity_t));
    memset(&deboun_time,0, sizeof(vm_dcl_eint_control_hw_debounce_t));

    status = vm_dcl_control(gpio_ctp_eint_handle ,VM_DCL_EINT_COMMAND_MASK,NULL);  /* Usually, before we config eint, we mask it firstly. */
    if(status != VM_DCL_STATUS_OK)
    {
    	vm_log_info("VM_EINT_CMD_MASK  = %d", status);
    }

	sens_data.sensitivity = 0;
	eint_config.act_polarity = 0;
	eint_config.auto_unmask = 1;

	status = vm_dcl_control(gpio_ctp_eint_handle ,VM_DCL_EINT_COMMAND_SET_SENSITIVITY,(void *)&sens_data);  /* set eint sensitivity */
	if(status != VM_DCL_STATUS_OK)
	{
		vm_log_info("VM_EINT_CMD_SET_SENSITIVITY = %d", status);
	}

	status = vm_dcl_control(gpio_ctp_eint_handle ,VM_DCL_EINT_COMMAND_MASK,NULL);  /* Usually, before we config eint, we mask it firstly. */
	if(status != VM_DCL_STATUS_OK)
	{
		vm_log_info("VM_EINT_CMD_MASK  = %d", status);
	}

    deboun_time.debounce_time = 1;  /* debounce time 1ms */
    status = vm_dcl_control(gpio_ctp_eint_handle ,VM_DCL_EINT_COMMAND_SET_HW_DEBOUNCE,(void *)&deboun_time); /* set debounce time */
    if(status != VM_DCL_STATUS_OK)
    {
        vm_log_info("VM_EINT_CMD_SET_HW_DEBOUNCE = %d", status);
    }

    status = vm_dcl_control(gpio_ctp_eint_handle ,VM_DCL_EINT_COMMAND_MASK,NULL);  /* Usually, before we config eint, we mask it firstly. */
    if(status != VM_DCL_STATUS_OK)
    {
    	vm_log_info("VM_EINT_CMD_MASK  = %d", status);
    }

	eint_config.debounce_enable = 0;    /* 1 means enable hw debounce, 0 means disable. */

	status = vm_dcl_control(gpio_ctp_eint_handle ,VM_DCL_EINT_COMMAND_CONFIG,(void *)&eint_config);   /* Please call this api finally, because we will unmask eint in this command. */
	if(status != VM_DCL_STATUS_OK)
	{
		vm_log_info("VM_EINT_CMD_CONFIG = %d", status);
	}

	//status = vm_dcl_control(gpio_ctp_eint_handle,VM_DCL_EINT_COMMAND_UNMASK,NULL);  /* call this function to unmask this eint. */
	//if(status != VM_DCL_STATUS_OK)
	//{
	//	 vm_log_info("VM_EINT_CMD_CONFIG = %d", status);
	//}
}

static VM_DRV_TP_BOOL ctp_goodix_gt9xx_init(void)
{
    VM_DCL_HANDLE sda_handle, scl_handle;
    VM_DCL_HANDLE eint_handle, reset_handle;
    VM_DRV_TP_BOOL ack;
    ctp_info_t ctp_info;
	VMUINT16 reg_value;

	VMUINT8 i=0;

	//turn on VSIM1
	*VSIM1_CON2 = *VSIM1_CON2 | 0x0002;
	*VSIM1_CON0 = *VSIM1_CON0 | 0x0001;

    // init i2c
    if(gpio_ctp_i2c_sda_pin != 0xFF)
    {
        sda_handle = vm_dcl_open(VM_DCL_GPIO, gpio_ctp_i2c_sda_pin);
        scl_handle = vm_dcl_open(VM_DCL_GPIO, gpio_ctp_i2c_scl_pin);

        vm_dcl_control(sda_handle, VM_DCL_GPIO_COMMAND_SET_MODE_0, NULL);
        vm_dcl_control(scl_handle, VM_DCL_GPIO_COMMAND_SET_MODE_0, NULL);
        CTP_DELAY_TIME = 3;
    }

    ctp_i2c_configure(CTP_SLAVE_ADDR, CTP_DELAY_TIME);

    eint_handle = vm_dcl_open(VM_DCL_GPIO, gpio_ctp_eint_pin);
    reset_handle = vm_dcl_open(VM_DCL_GPIO, gpio_ctp_reset_pin);

	////////////////////////////reset//////////////////////////////////
	vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_SET_DIRECTION_OUT, NULL);
	vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_WRITE_LOW, NULL);

	vm_dcl_control(reset_handle, VM_DCL_GPIO_COMMAND_SET_MODE_0, NULL);
	vm_dcl_control(reset_handle, VM_DCL_GPIO_COMMAND_SET_DIRECTION_OUT, NULL);
	vm_dcl_control(reset_handle, VM_DCL_GPIO_COMMAND_WRITE_LOW, NULL);
	delay_ms(5);
	vm_dcl_control(reset_handle, VM_DCL_GPIO_COMMAND_WRITE_HIGH, NULL);
	delay_ms(10);
	//////////////////////////////////////////////////////////////////
	vm_dcl_close(eint_handle);
	vm_dcl_close(reset_handle);

	//vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_SET_DIRECTION_IN, NULL);
	//vm_dcl_control(eint_handle, gpio_ctp_eint_pin_M_EINT, NULL);
	//EINT_Set_Sensitivity(custom_eint_get_channel(touch_panel_eint_chann), EDGE_SENSITIVE);
	ctp_init_EINT();

	ack = ctp_goodix_gt9xx_set_configuration();
	if(ack == VM_DRV_TP_FALSE)
	{
		vm_log_info( "ctp_goodix_gt9xx_set_configuration fail!!!" );
    	return VM_DRV_TP_FALSE;
	}
	vm_log_info( "ctp_goodix_gt9xx_set_configuration OK!!!" );

	ack = ctp_goodix_gt9xx_get_information( &ctp_info );

    if ( ack == VM_DRV_TP_FALSE )
    {
        vm_log_info("read information fail" );
        return VM_DRV_TP_FALSE;
    }

    return VM_DRV_TP_TRUE;

}

static VM_DRV_TP_BOOL ctp_goodix_gt9xx_parameters(vm_drv_tp_parameters_t *para, VMUINT32 get_para, VMUINT32 set_para)
{
	return VM_DRV_TP_TRUE;
}

static VM_DRV_TP_BOOL ctp_read_one_point(VMUINT32 x_base, vm_drv_tp_single_event_t *event)
{
	VMUINT8 point_info[CTP_POINT_INFO_LEN];
	CTP_I2C_read(x_base, point_info, CTP_POINT_INFO_LEN);

	event->x = point_info[1] + ( point_info[2] << 8);
	event->y = point_info[3] + ( point_info[4] << 8);
	event->z = 32;
	//vm_log_info( "id:%d", point_info[0]);

	return VM_DRV_TP_TRUE;

}

static VM_DRV_TP_BOOL ctp_read_all_point(vm_drv_tp_multiple_event_t *tpes, VMUINT32 points)
{
    VMUINT32 i=0;
    vm_drv_tp_single_event_t get_one_point;

    if((points<1) || (points>5))
    {
        return VM_DRV_TP_FALSE;
    }

    for(i=0;i<points;i++)
    {
        ctp_read_one_point( CTP_POINT_INFO_REG_BASE + CTP_POINT_INFO_LEN*i , &get_one_point);
        tpes->points[i].x = 239 - get_one_point.x;
        tpes->points[i].y = get_one_point.y;
        tpes->points[i].z = get_one_point.z;
    }

    return VM_DRV_TP_TRUE;

}


VM_DRV_TP_BOOL ctp_goodix_gt9xx_get_data(vm_drv_tp_multiple_event_t *tpes)
{
	VMUINT8  lvalue = 0;
	VMUINT32 model = 0;
	VMUINT8  buf_status;
	VM_DRV_TP_BOOL   ret = VM_DRV_TP_TRUE;
	VMUINT8  rst_char = 0;

	tpes->time_stamp = (VMUINT16)vm_drv_tp_get_time_stamp();
	tpes->padding = VM_DRV_TP_PATTERN;

	CTP_I2C_read( CTP_TOUCH_INFO_REG_BASE, &lvalue, 1);

	model = (VMUINT32)(lvalue & CTP_STAT_NUMBER_TOUCH);
	buf_status = lvalue&0xF0;

	//vm_log_info( "model:%d buf_status:%d", model,buf_status);


    if(model > 5) //goodix only support 5 fingers
    {
        ret = VM_DRV_TP_FALSE;
        goto exit_get_data;
    }

	tpes->model = (VMUINT16)model;  //read out all touch points coordinates.

	if (model == 0)
	{
		ret = VM_DRV_TP_FALSE;
		CTP_I2C_read( CTP_POINT_INFO_REG_BASE + 1 , &lvalue, 1);
		goto exit_get_data;
	}

	if ( model <= 10 )
	{
		if ( ( buf_status & 0x80 ) != 0 )
		{
			ctp_read_all_point(tpes, model);
		}
		else
		{
			goto exit_get_data;
		}
	}
	else
	{
		if(model == 0x0d)
		{
			//TODO: check this event
		}
		else if(model == 0x0e)
		{
			//TODO: check this event
		}
		else
		{
			//TODO: check this event
		}

		goto exit_get_data;
	}

exit_get_data:

	CTP_I2C_send( CTP_TOUCH_INFO_REG_BASE , &rst_char, 1);

	return ret;

}

static VM_DRV_TP_BOOL ctp_goodix_gt9xx_set_device_mode(VM_DRV_TP_DEVICE_MODE mode)
{
	VMUINT8 suspend_command = 0x05;
	VM_DCL_HANDLE eint_handle;
	ctp_info_t ctp_info;
	VMUINT8 ctp_buffer[2] = {0};

	if( mode == VM_DRV_TP_ACTIVE_MODE )
	{
		eint_handle = vm_dcl_open(VM_DCL_GPIO, gpio_ctp_eint_pin);
		vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_SET_MODE_0, NULL);
		vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_SET_DIRECTION_OUT, NULL);
		vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_WRITE_HIGH, NULL);
		delay_ms(15);
		vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_SET_MODE_2, NULL);
		vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_SET_DIRECTION_IN, NULL);
		vm_dcl_close(eint_handle);
	}
	else if( (mode == VM_DRV_TP_SLEEP_MODE))
	{
		eint_handle = vm_dcl_open(VM_DCL_GPIO, gpio_ctp_eint_pin);
		vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_SET_MODE_0, NULL);
		vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_SET_DIRECTION_OUT, NULL);
		vm_dcl_control(eint_handle, VM_DCL_GPIO_COMMAND_WRITE_LOW, NULL);
		delay_ms(5);
		CTP_I2C_send( CTP_POWER_MODE_REG, &suspend_command, 1 );
		CTP_I2C_read(CTP_POWER_MODE_REG,ctp_buffer,1);
		vm_dcl_close(eint_handle);
	}
	else if (mode == VM_DRV_TP_FIRMWARE_UPDATE )
	{
	      // need wdt
	}

	return VM_DRV_TP_FALSE;

}

static VMUINT32 ctp_goodix_gt9xx_command(VMUINT32 cmd, void* p1, void* p2) // p1: input p2: output
{
	#define TPD_SYNC_REG                  0x711
	#define TPD_DIFF_DATA_REG             0xD80
	#define TPD_CHANNEL_CONFIG_REG        0x6D5
	#define TPD_RAW_DATA_1_REG            0x880
	#define TPD_RAW_DATA_2_REG            0x9C0

	#define MAX_DRIVING_CHANNEL 16
	#define MAX_SENSING_CHANNLE 10
	#define MAX_DIFF_DATA_SIZE   (MAX_DRIVING_CHANNEL*MAX_SENSING_CHANNLE*2)
	#define MAX_RAW_DATA_SIZE    MAX_DIFF_DATA_SIZE

	char **dst = (char **)p2;
	ctp_info_t ctp_info;
	VM_DRV_TP_BOOL ret = VM_DRV_TP_TRUE;
	VMUINT8 *cfg = (VMUINT8 *)p1;
	VMUINT8 mode;
	VMUINT8 *tpd_raw_data;
	VMUINT8 tmp[MAX_DIFF_DATA_SIZE];
	VMUINT8 *ptr;
	VMUINT8 row;
	VMUINT8 illegal = 0;

	if(dst != NULL)
		*dst = NULL;

	switch ( cmd )
	{
		case VM_DRV_TP_COMMAND_GET_VERSION: // get firmware version
			if ( ctp_goodix_gt9xx_get_information(&ctp_info) )
			{
				memcpy( tpd_raw_data, (VMUINT8 *)&ctp_info, sizeof( ctp_info_t ) );
				*dst = (char *)tpd_raw_data;
			}
			else
			{
				ret = VM_DRV_TP_FALSE;
			}
			break;

		case VM_DRV_TP_COMMAND_GET_CONFIG:
			if ( 	CTP_I2C_read( CTP_CONFIG_REG_BASE, tpd_raw_data, VM_DRV_TP_CONFIG_LENGTH ) )
			{
				*dst = (char *)tpd_raw_data;
			}
			else
			{
				ret = VM_DRV_TP_FALSE;
			}
			break;

		case VM_DRV_TP_COMMAND_LOAD_INT_CONFIG:
			ctp_goodix_gt9xx_set_configuration();
			break;

		case VM_DRV_TP_COMMAND_LOAD_EXT_CONFIG:
			cfg[105] = 1;
			CTP_I2C_send( CTP_CONFIG_REG_BASE, cfg, VM_DRV_TP_CONFIG_LENGTH );
			break;

		case VM_DRV_TP_COMMAND_GET_DIFF_DATA:
			break;

		case VM_DRV_TP_COMMAND_GET_FW_BUFFER:
			break;

		case VM_DRV_TP_COMMAND_DO_FW_UPDATE:
			break;
		case 8:
			CTP_I2C_read( CTP_TOUCH_INFO_REG_BASE, &illegal, 1 );
			if((illegal & 0x80) == 0)
				ret = 1;
			else
				ret = 0;
			break;
		default:
			ret = VM_DRV_TP_FALSE;
	}

	return ret;

}

static vm_drv_tp_function_list_t ctp_custom_func=
{
	ctp_goodix_gt9xx_init,
	ctp_goodix_gt9xx_set_device_mode,
	ctp_goodix_gt9xx_hisr,
	ctp_goodix_gt9xx_get_data,
	ctp_goodix_gt9xx_parameters,
	ctp_goodix_gt9xx_power,
	ctp_goodix_gt9xx_command
};

void tp_gt9xx_init(void)
{

    gpio_ctp_eint_handle = vm_dcl_open(VM_DCL_EINT, 3);

	if(VM_DCL_HANDLE_INVALID == gpio_ctp_eint_handle)
	{
		vm_log_info("open EINT error");
		return;
	}

	vm_drv_tp_setup_driver(&ctp_custom_func);
}
